EGT modelling of peer-feedback and genAI invasion

EGT modelling of cooperation in classroom with LLM

Modelling cooperation amongst peers

The model examines the relationship between peers in a learning environment providing feedback on each others work. In this model the agents are students which we will refer to as learners, and interactions are between learners choosing to provide and receive feedback from each other. We use \(P\) to indicate the learner providing feedback, and \(R\) the learner receiving feedback.

The strategy of interacting with other learners is outlined initially with a single parameter:

  • \(\Theta \in[0,1]\): Level of cooperative effort - that is how much effort a learner puts into providing feedback to another learner. The more effort towards cooperation the more benefit a peer receives from the feedback, and the more cost is incurred by the provider. \(\Theta_P\) indicates the collaborative effort of the learner providing the feedback, and \(\Theta_R\) the collaborative effort of the incoming feedback learner receiving the feedback.

The environment is balanced by three parameters that guide the payoffs of the different strategies:

  • \(c\) - Cost of cooperation
  • \(f\) - Benefit to yourself from others providing feedback / cooperating with you.
  • \(b\) - Benefit to yourself for providing others with feedback (i.e. you learn by providing feedback)
  • \(a\) - Benefit to yourself if you are cooperating with someone with the same strategy as you (i.e. a social benefit, gravitation towards similar learners).

The model initially explores the scenario \(c=4,f=5, b=2,a=1\), and will generally assume that \(f > c\). We explore varying strategies and varying the value of \(b\).

In this framework if a player with effort strategy \(\Theta_R\) receives feedback from someone with effort strategy \(\Theta_P\) they gain some perceived value, \(V(R|P)\) (read as value to reciever \(R\) when they meet provider \(P\)), out of sharing feedback with a peer:

\[V(R|P) = \Theta_P f + \Theta_R b - \Theta_Rc +a[RP]\]

Where \(a[RP]=a\) if \(\Theta_R=\Theta_P\), and zero otherwise. This amounts to a preference for working with like-minded peers. Alternatively \(a[RP]\) can specified as \(a(1-|\Theta_R-\Theta_P|)\), which is fine mathematically but still requires some thinking.

\[ V(R|P) = \Theta_P f + \Theta_R b - \Theta_Rc +a(1-|\Theta_R-\Theta_P|) \]

So the learner gains (or at least perceives to gain) value from the knowledge from the feedback at a level dependent on the providers effort (\(f\Theta_P\)), as well as from providing feedback to them depending their own effort towards cooperation (\(b\Theta_R\)). They incur a cost relative to the effort they put into cooperation (\(c\Theta_R\)).

Within this framework there are three strategies that agents can take that we will examine:

  • \(C\): Cooperation, where cooperative effort is highest, \(\Theta_C=1\)
  • \(T\): Token-effort, where cooperative effort is half-way, \(\Theta_T=0.5\)
  • \(F\): Free-rider, with no cooperative effort, \(\Theta_F=0\)

To introduce genAI we provide two new strategy parameters, indicating the use of genAI for providing feedback to others, or using genAI to supplement feedback a learner receives:

  • \(\Pi\in\{0,1\}\): The use of genAI to provide feedback (1) or not (0). \(\Pi_P\) indicates the choice of the provider of feedback.
  • \(\Gamma \in \{0,1\}\): The use of genAI to analyse your own work (1) or not (0). \(\Gamma_R\) indicates the choice of the receiver.

Additionally, there are two new environment parameters:

  • \(q\): The quality of the genAI for feedback.
  • \(k\): The reduced cost of providing feedback using AI.

We use two values, \(q=0.8\) to indicate slightly worse than what the peer would provide (with full effort), or \(q=1.25\) for slightly better than what you would expect from peers. We use \(k=0.1\) to indicate a low cost (compared to providing the feedback yourself).

This adds four new strategies of genAI use (note that the paper only introduces one)

  • \(N\): No genAI use, \(\Pi=0,\Gamma=0\).
  • \(S\): Using genAI for only your own work, \(\Pi=0,\Gamma=1\). This incurs an additional cost \(c\times k\), but also provides a new benefit \(f \times q\).
  • \(O\): Using genAI only to provide feedback for others, \(\Pi=1, \Gamma = 0\). This reduces the cost of providing feedback to \(c \times k\) (as \(0<k<1\)), but also changes the value of the feedback to \(f \times q\) instead of \(f \times \Theta_P\).
  • \(B\): Using genAI for yourself and others, \(\Pi=1,\Gamma=1\). This combines the effects of \(S\) and \(O\).

We then combine the cooperation strategies and genAI strategies. So \(CN\) indicates that the learner is fully cooperating but not using genAI for themselves or for providing feedback. \(FS\) would indicate that a Free-rider (no effort towards cooperation) is using the genAI for their own feedback. Note that the strategies \(FO\) and \(FB\) do not make sense - a Free-rider is not providing feedback for others so would not bother using genAI for it. They might move towards \(TS\) or \(TB\) however.

The new, extended value calculation is more complicated:

\[\begin{align} V(R|P) = \ &(1-\Pi_P + q\Pi_P )\Theta_Pf \ + \\ &(1-\Pi_R + k \Pi_R)\Theta_R (b - c) \ + \\ &(1-|\Theta_R-\Theta_P|)a \ + \\ &\Gamma_R (qf - kc) \end{align}\]

It takes a bit of looking, but the above formula, for \(\Gamma_R=\Pi_R=\Pi_P=0\) this formula reduces to the non-genAI scenario (note that \(\Gamma_P\) is irrelevant for calculating the receivers payoff).

In the paper, the parameter \(\Gamma\) is omitted - it is assumed that everyone is using the same strategy for their own GenAI feedback and that this is balancing out.

Furthermore, although this exploration chooses specific values of \(\Theta\), \(\Pi\) and \(\Gamma\), the model allows for continuous versions of these strategies.

Instantiating the game

The game is instantiated in the code below:

Show the code
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.0     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Show the code
# library(EvolutionaryGames)

get_payoff <- function(
        # Strat_r: Receiving strat, Strat_p: Providing strat
    Strat_r, Strat_p, # expects rows of data frame with columns Eff, AIP, AIS
    p = list(
        # Parameters
        c = 4, # cost
        f = 5, # receive benefit - c <> b_r might depend on knowledge gap??
        b = 2, # self benefit
        q = 0.8, # LLM is 0.8 of decent feedback
        k = 0.1, # reduces cost of giving fb by this
        a = 1
    )) {
    # Relabelling to match model formula
    Theta_R = Strat_r$Eff
    Theta_P = Strat_p$Eff
    Gamma_R = Strat_r$AIS
    Pi_R = Strat_r$AIP
    Pi_P = Strat_p$AIP
    f <- p$f
    b <- p$b
    c <- p$c
    a <- p$a
    q <- p$q
    k <- p$k
    
    V.f <- (1-Pi_P + q * Pi_P ) * Theta_P + Gamma_R * q
    V.bc <- ((1 - Pi_R) + Pi_R * k) * Theta_R
    V.a <- 1 - abs(Theta_R - Theta_P)
    
    V = V.f * f + 
        V.bc * (b - c) + 
        V.a * a + 
        Gamma_R * (q * f - k * c)
    return(V)
}
Show the code
# game modelling
all_strategies <- tribble(
    ~Label, ~Eff, ~AIS, ~AIP,
    "CN", 1,   0, 0,
    "TN", 0.5, 0, 0,
    "FN", 0,   0, 0,
    "CS", 1,   1, 0,
    "TS", 0.5, 1, 0,
    "FS", 0,   1, 0,
    "CO", 1,   0, 1,
    "TO", 0.5, 0, 1,
    "FO", 0,   0, 1,
    "CB", 1,   1, 1,
    "TB", 0.5, 1, 1,
    "FB", 0,   1, 1,
    "CO.8", 1, 0, 0.8,
    "CO.6", 1, 0, 0.6,
    "CO.4", 1, 0, 0.4,
    "CO.2", 1, 0, 0.2
)
build_payoff_matrix <- function(
        S,
        # Parameters
        p = list(
            c = 2, # cost
            f = 2, # receive benefit
            b = 1, # self benefit
            q = 0.8, # LLM is 0.8 of decent feedback
            k = 0.1 # reduces cost of giving fb by this, but also reduces self benefit
        )
) {
    N_strategies <- nrow(S)
    payoffs <- array(rep(NA_real_, N_strategies*N_strategies), dim = c(N_strategies, N_strategies)) 
    for (R in 1:N_strategies) {
        for (P in 1:N_strategies) {
            payoffs[R,P] <- get_payoff(slice(S, R), slice(S, P), p)
        }
    }
    return(payoffs)
}

print_payoff_matrix <- function(M, stgys) {
    rownames(M) <- stgys
    colnames(M) <- stgys
    print(M)
}

fetch_payoff_matrix <- function(strategies, p, as_df = FALSE,
                                add_exp_V = FALSE) {
    # strategies: character vector, p: list
    S.df <- tibble(Label = strategies) |> # preserves order of strategies
        inner_join(all_strategies, by = "Label")
    payoff_matrix <- build_payoff_matrix(S.df, p)
    if (as_df) {
        colnames(payoff_matrix) <- strategies
        payoff_df <- as_tibble(payoff_matrix) |> 
            mutate(`Payoff to strategy:` = strategies) |> 
            select(`Payoff to strategy:`, everything())
        if (add_exp_V) {
            payoff_df <- payoff_df |> 
                rowwise() |> 
                mutate(ExpV = mean(c_across(where(is.numeric))))
        }
        return(payoff_df)
    } else {
        return(payoff_matrix)
    }
}

No AI

Comparing Free-rider, Token (half effort) and Cooperator.

Initially with \(c=4,f=5,b=2, a=1\).

Show the code
# Model params
        p = list(
            c = 4, # cost
            f = 5, # receive benefit
            b = 2, # self benefit
            q = 0.8, # No value from AI
            k = 0.1, # Cost of using AI
            a = 1 # assortment
        )
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 2
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1

Comparing just \(FN\) and \(CN\) (i.e. Hawk-Dove)

Show the code
strategies <- c("CN", "FN")
fetch_payoff_matrix(strategies, p, as_df = T)
# A tibble: 2 × 3
  `Payoff to strategy:`    CN    FN
  <chr>                 <dbl> <dbl>
1 CN                        4    -2
2 FN                        5     1

The overall fitness of the population is given, for proportion of \(p\) cooperators, by \(W(p)=2p^2+2p(1-p)=2p\), so we want to have \(p\) as high as possible. But from the point of view of the individual it is much nicer to choose \(FN\).

We can also include TN to see how the dynamics evolve over time.

Show the code
strategies <- c("TN", "CN", "FN")
payoffs_df <- fetch_payoff_matrix(strategies, p, as_df = T)
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
payoffs_df
# A tibble: 3 × 4
  `Payoff to strategy:`    TN    CN    FN
  <chr>                 <dbl> <dbl> <dbl>
1 TN                      2.5   4.5  -0.5
2 CN                      1     4    -2  
3 FN                      3     5     1  
Show the code
strategies <- c("T", "C", "F")
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

From this we can see that a population evenly mixed between the three strategies (in the centre of the triangle) would move away from the cooperative strategy \(CN\) and slowly veer towards a population of free-riders, \(FN\).

Introducing AI

Three plots each for lower AI quality and higher AI quality. Many combos, so assumptions about behaviour:

Show the code
fetch_payoff_matrix(
    all_strategies$Label, p, as_df = T, add_exp_V = T) |> arrange(ExpV)
# A tibble: 16 × 18
# Rowwise: 
   `Payoff to strategy:`    CN    TN    FN    CS    TS    FS    CO    TO    FO
   <chr>                 <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1 CN                     4     1    -2     4     1    -2     3     0.5  -2   
 2 CO.2                   4.36  1.36 -1.64  4.36  1.36 -1.64  3.36  0.86 -1.64
 3 CO.4                   4.72  1.72 -1.28  4.72  1.72 -1.28  3.72  1.22 -1.28
 4 TN                     4.5   2.5  -0.5   4.5   2.5  -0.5   3.5   2    -0.5 
 5 CO.6                   5.08  2.08 -0.92  5.08  2.08 -0.92  4.08  1.58 -0.92
 6 CO.8                   5.44  2.44 -0.56  5.44  2.44 -0.56  4.44  1.94 -0.56
 7 FN                     5     3     1     5     3     1     4     2.5   1   
 8 FO                     5     3     1     5     3     1     4     2.5   1   
 9 CO                     5.8   2.8  -0.2   5.8   2.8  -0.2   4.8   2.3  -0.2 
10 TO                     5.4   3.4   0.4   5.4   3.4   0.4   4.4   2.9   0.4 
11 CS                    11.6   8.6   5.6  11.6   8.6   5.6  10.6   8.1   5.6 
12 TS                    12.1  10.1   7.1  12.1  10.1   7.1  11.1   9.6   7.1 
13 FS                    12.6  10.6   8.6  12.6  10.6   8.6  11.6  10.1   8.6 
14 FB                    12.6  10.6   8.6  12.6  10.6   8.6  11.6  10.1   8.6 
15 CB                    13.4  10.4   7.4  13.4  10.4   7.4  12.4   9.9   7.4 
16 TB                    13    11     8    13    11     8    12    10.5   8   
# ℹ 8 more variables: CB <dbl>, TB <dbl>, FB <dbl>, CO.8 <dbl>, CO.6 <dbl>,
#   CO.4 <dbl>, CO.2 <dbl>, ExpV <dbl>
  • For any strategy \(X\), \(E[V(XS)]>E[V(XO)]>E[V(XN)]\), so genAI strategies of none, \(XN\), or others only, \(XO\), are ignored. This is the high cost benefit ratio of using the genAI for your own feedback. Maybe a supplementary proof??
  • \(FB=TB=CB\), so these will be treated as \(FB\). Note that we have used \(FB\) as this is not really putting in effort to feedback - the genAI is being used to provide the feedback.

Change in thinking: given the fixed benefit of genAI for self, it is easier communicate the results looking at no genAI for self (effectively dropping the \(\Gamma\) parameter).

Show the code
interesting_strategies <- all_strategies |> 
    filter(!str_detect(Label, "S|B"), 
           Label != "FB", Label != "TS", Label != "CS") |> 
    arrange(Label) |> 
    pull(Label)
interesting_strategies
 [1] "CN"   "CO"   "CO.2" "CO.4" "CO.6" "CO.8" "FN"   "FO"   "TN"   "TO"  

This leaves 10: ,

Show the code
fetch_payoff_matrix(
    interesting_strategies, p, as_df = T, add_exp_V = T) |> arrange(ExpV)
# A tibble: 10 × 12
# Rowwise: 
   `Payoff to strategy:`    CN    CO  CO.2  CO.4  CO.6  CO.8    FN    FO    TN
   <chr>                 <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1 CN                     4     3     3.8   3.6   3.4   3.2  -2    -2     1   
 2 CO.2                   4.36  3.36  4.16  3.96  3.76  3.56 -1.64 -1.64  1.36
 3 CO.4                   4.72  3.72  4.52  4.32  4.12  3.92 -1.28 -1.28  1.72
 4 TN                     4.5   3.5   4.3   4.1   3.9   3.7  -0.5  -0.5   2.5 
 5 CO.6                   5.08  4.08  4.88  4.68  4.48  4.28 -0.92 -0.92  2.08
 6 CO.8                   5.44  4.44  5.24  5.04  4.84  4.64 -0.56 -0.56  2.44
 7 FN                     5     4     4.8   4.6   4.4   4.2   1     1     3   
 8 FO                     5     4     4.8   4.6   4.4   4.2   1     1     3   
 9 CO                     5.8   4.8   5.6   5.4   5.2   5    -0.2  -0.2   2.8 
10 TO                     5.4   4.4   5.2   5     4.8   4.6   0.4   0.4   3.4 
# ℹ 2 more variables: TO <dbl>, ExpV <dbl>

Increasing self benefit in no-AI environment

No AI. \(c>b=2\)

Parameters:

Show the code
p$b <- 2
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 2
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TN", "CN", "FN")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

No AI \(c=b=4\)

Parameters:

Show the code
p$b <- 4
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 4
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TN", "CN", "FN")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

No AI \(c<b=6\)

Parameters:

Show the code
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TN", "CN", "FN")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

Increasing self benefit in genAI environment - cooperators using AI

With AI: \(c>b=2\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 2
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 2
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With AI: \(c=b=4\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 4
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 4
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With AI \(c<b=6\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

Increasing self benefit in genAI environment - cooperators do not use AI

\(c>b=2\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 2
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 2
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CN", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With AI: \(c=b=4\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 4
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 4
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CN", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With AI \(c<b=6\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CN", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

Increasing self benefit in superior genAI environment - cooperators use AI

\(c>b=2\)

Parameters:

Show the code
p$q <- 1.25
p$b <- 2
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 2
 $ q: num 1.25
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With superior AI and \(c=b=4\)

Parameters:

Show the code
p$q <- 1.25
p$b <- 4
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 4
 $ q: num 1.25
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With superior AI and \(c<b=6\)

Parameters:

Show the code
p$q <- 1.25
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 1.25
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

Cooperation, but metered use of GenAI by cooperators

Changing how much GenAI is being used by teh \(CO\) strategy. So \(CO.8\) is setting \(\Pi=0.8\).

With AI and \(c<b=6\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO.8", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With AI and \(c<b=6\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO.6", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With AI and \(c<b=6\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO.4", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()

With AI and \(c<b=6\)

Parameters:

Show the code
p$q <- 0.8
p$b <- 6
glimpse(p)
List of 6
 $ c: num 4
 $ f: num 5
 $ b: num 6
 $ q: num 0.8
 $ k: num 0.1
 $ a: num 1
Show the code
strategies <- c("TO", "CO.2", "FO")
payoffs_M <- fetch_payoff_matrix(strategies, p, as_df = F)
Show the code
import numpy as np
import matplotlib.pyplot as plt
from egttools.plotting.simplified import plot_replicator_dynamics_in_simplex, plot_pairwise_comparison_rule_dynamics_in_simplex_without_roots
from egttools.utils import calculate_stationary_distribution

payoffs = np.array(r.payoffs_M)
type_labels = r.strategies

fig, ax = plt.subplots(figsize=(15,10))
simplex, gradient_function, roots, roots_xy, stability = plot_replicator_dynamics_in_simplex(payoffs, nb_of_initial_points_for_root_search=100, 
ax=ax)

plot = (simplex.add_axis(ax=ax)
           .draw_triangle()
           .draw_gradients(zorder=0)
           # .add_colorbar()
           .add_vertex_labels(type_labels)
           .draw_stationary_points(roots_xy, stability))

ax.axis('off')
(np.float64(0.0), np.float64(1.0), np.float64(0.0), np.float64(0.9093266739736605))
Show the code
ax.set_aspect('equal')
plt.xlim((-.05,1.05))
(-0.05, 1.05)
Show the code
plt.ylim((-.02, simplex.top_corner + 0.05))
(-0.02, 0.9160254037844386)
Show the code
plt.show()